Introduction
Pyscript/Pyodide supports excellent interoperability between JavaScript and Python. Python can call JavaScript and JavaScript can call Python. DOM events can use Python functions as callbacks. This article covers Python calling JavaScript functions and how to pass and receive data using Proxy. In another article, I cover calling Python from JavaScript and how to use Python in events.
Pyscript: JavaScript Event Callbacks
Pyodide attaches JavaScript functions to the global namespace. To import the global namespace into Python include this line:
1 |
import js |
If you will be calling async JavaScript functions, also add this line:
1 |
import asyncio |
Calling JavaScript from Python
This example defines a JavaScript function square()
that is called from Python. The key is to import the module js
and call JavaScript functions using the js
namespace. This is a complete example that you can copy and paste into a local file and load into the browser. I prefer to start a simple Python webserver to run examples: python -m http.server 9000
. Future examples will not include the HTML boilerplate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<!DOCTYPE html> <html> <head> <title>Example 1</title> <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" /> <script defer src="https://pyscript.net/alpha/pyscript.js"></script> </head> <body> <py-script> import js # Call a JS function result = js.square(4) print(result) </py-script> <script type="text/javascript"> function square(number) { return number * number; } </script> </body> </html> |
Calling Async JavaScript from Python
The key to calling JavaScript async functions is to import asyncio
and use the await keyword as shown in this example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<body> <py-script> import js import asyncio result = await js.square(4) print(result) </py-script> <script type="text/javascript"> async function square(number) { return number * number; } </script> </body> |
This is a more complex example that calls the JavaScript fetch function. Notice how to await async function completion. This example will display your public IP address.
1 2 3 4 5 6 7 8 9 10 11 |
<body> <py-script> import js import asyncio url = "https://api.ipify.org" result = await (await js.fetch(url)).text() print('My Public IP: {}'.format(result)) </py-script> </body> |
JavaScript returning an object
When a JavaScript function returns an object to a Python caller, a JsProxy
is returned. If you try to iterate the proxy, an error is reported.
1 |
File "", line 5, in TypeError: 'pyodide.JsProxy' object is not iterable ) |
An example that fails:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<body> <py-script> import js result = js.get_object() for r in result: print(r) </py-script> <script type="text/javascript"> function get_object() { return { "a": 4, "b": "4" }; } </script> </body> |
The solution is to call object.to_py()
. This example works correctly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<body> <py-script> import js result = js.get_object().to_py() for r in result: print(r) </py-script> <script type="text/javascript"> function get_object() { return { "a": 4, "b": "4" }; } </script> </body> |
Passing an object to JavaScript
If you pass a Python object to a JavaScript function, the function receives a Proxy. To get individual keys, call obj.get(“keyname”):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<body> <py-script> import js obj = { "a": 4, "b": "8" } js.put_object(obj) </py-script> <script type="text/javascript"> function put_object(obj) { // obj is a Proxy console.log("a = " + obj.get("a")); console.log("b = " + obj.get("b")); } </script> </body> |
Another option is to import the to_js()
helper and covert the Python object.
This is similar to PyProxy.toJs, but for use from Python. If the object can be implicitly translated to JavaScript, it will be returned unchanged. If the object cannot be converted into JavaScript, this method will return a JsProxy of a PyProxy, as if you had used pyodide.create_proxy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<body> <py-script> import js from pyodide import to_js obj = { "a": 4, "b": "8" } js.put_object(to_js(obj)) </py-script> <script type="text/javascript"> function put_object(obj) { // obj is a Map for(var [key, val] of obj) { console.log("Key: " + key, " Value: " + val); } } </script> </body> |
Convert a dictionary to a JavaScript object
To convert dictionaries, use the Object.fromEntries()
API.
The fromEntries()
method transforms a list of key-value pairs (Python dictionary) into an object.
1 2 3 |
from js import Object headers = { "Content-type": "application/json" } objects = Object.fromEntries(to_js(headers)) |
An example is calling the JavaScript fetch()
API. The fetch()
API requires that the headers parameter be a JavaScript object.
1 2 3 4 5 6 7 8 9 10 |
response = await js.fetch( url, method="POST", body=json.dumps(body), redirect="manual", headers=Object.fromEntries(to_js(headers)) ) j = (await response.json()).to_py() status = response.status |
Converting Python BytesIO Object to JavaScript Blob
To convert a BytesIO object to a Blob, often used with the file API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from js import Blob, window from pyodide import to_js # Read the BytesIO object and convert the data to JavaScript content = to_js(buf.read()) # Create a Blob from the content object and specify its content type b = Blob.new([content], {type: "image/png"}) # Example Blob usage fileHandle = await window.showSaveFilePicker() file = await fileHandle.createWritable() await file.write(b) await file.close() |
Summary
Hopefully, this introduction helps you understand how to call JavaScript functions from Python and pass and receive data including objects. As Pyscript develops, I am sure there will be many more automatic conversions and more functions to support type conversion.
More Information
- Other articles that I have written on Pyscript
- Pyodide Version 0.20 Documentation
- to_js()
- Object.fromEntries()
Photography Credit
I write free articles about technology. Recently, I learned about Pexels.com which provides free images. The image in this article is courtesy of Ashithosh U at Pexels.
I design software for enterprise-class systems and data centers. My background is 30+ years in storage (SCSI, FC, iSCSI, disk arrays, imaging) virtualization. 20+ years in identity, security, and forensics.
For the past 14+ years, I have been working in the cloud (AWS, Azure, Google, Alibaba, IBM, Oracle) designing hybrid and multi-cloud software solutions. I am an MVP/GDE with several.
May 13, 2022 at 5:37 AM
This is exactly what I was looking for !! Thanks John !!